package cn.itcast.nems.oldems.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.nems.base.api.DiscountsApi;
import cn.itcast.nems.base.dao.dataobject.ClazzDO;
import cn.itcast.nems.base.dao.mapper.ClazzMapper;
import cn.itcast.nems.base.dto.DiscountTotalDTO;
import cn.itcast.nems.base.dto.DiscountsDTO;
import cn.itcast.nems.base.dto.DiscountsQueryDTO;
import cn.itcast.nems.base.util.ProductBeanUtil;
import cn.itcast.nems.common.PageQueryModel;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.common.util.LocalDateTimeUtils;
import cn.itcast.nems.discount.api.DiscountApi;
import cn.itcast.nems.discount.dto.DiscountSummaryDTO;
import cn.itcast.nems.discounts.dao.dataobject.EmsDiscountApplyScopeDO;
import cn.itcast.nems.discounts.dao.dataobject.EmsDiscountsDO;
import cn.itcast.nems.discounts.dao.entiry.EmsDiscounts;
import cn.itcast.nems.discounts.dao.mapper.EmsDiscountsMapper;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.oldems.service.DiscountsService;
import cn.itcast.nems.product.dao.entity.Product;
import cn.itcast.nems.product.dao.mapper.ProductMapper;
import cn.itcast.nems.product.scheduler.TuitionFeeService;
import cn.itcast.nems.studentrecord.service.DiscountCalculator;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class DiscountsServiceImpl extends ServiceImpl<EmsDiscountsMapper, EmsDiscounts> implements DiscountsService, DiscountsApi {

    private final ProductMapper productMapper ;
    private final ClazzMapper clazzMapper ;
    private final TuitionFeeService tuitionFeeService ;
    private final DiscountApi discountApi ;
    @Override
    public List<DiscountsDTO> findDiscountsByParentId(Integer parentId) {
        List<EmsDiscountsDO> list = getBaseMapper().findDiscountsByParentId(parentId) ;
        return CollectionUtil.isEmpty(list)?null :
                list.stream().map(this::transferDiscountDTO).collect(Collectors.toList());
    }

    @Override
    public IPage<EmsDiscountsDO> findPageList(Page<EmsDiscountsDO> page, EmsDiscountsDO discountsDO) {
        IPage<EmsDiscountsDO> result = getBaseMapper().findPageList(page, discountsDO);
        this.appendApplyScope(result.getRecords());
        return result;
    }

    @Override
    public EmsDiscountsDO queryById(Integer id) {
        var record = super.getById(id);
        Assert.notNull(record, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "优惠券在"));
        EmsDiscountsDO result = CustomBeanUtil.copyProperties(record, new EmsDiscountsDO());
        this.appendApplyScope(List.of(result));
        return result;
    }

    @Override
    public List<EmsDiscountsDO> queryByIds(@NonNull Set<Integer> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        var records = super.listByIds(ids);
        Assert.isTrue(ids.size() == records.size(), BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "优惠券在"));
        return records.stream()
                .map(o -> CustomBeanUtil.copyProperties(o, new EmsDiscountsDO()))
                .toList();
    }

    /**
     * 追加优惠适用范围(效率差, 后续优化)
     *
     * @param discountList 优惠集合
     * @author 高昌露
     * @since 2023/12/7 10:04
     */
    private void appendApplyScope(@NonNull List<EmsDiscountsDO> discountList) {
        if (CollectionUtil.isEmpty(discountList)) {
            return;
        }
        Map<Integer, EmsDiscountsDO> discountMap = discountList.stream().collect(Collectors.toMap(EmsDiscountsDO::getId, Function.identity()));
        List<EmsDiscountApplyScopeDO> emsDiscountApplyScopeDOList = super.getBaseMapper().queryDiscountApplyScopeByDiscountIds(discountMap.keySet());
        Map<Integer, List<EmsDiscountApplyScopeDO>> allDataMap = emsDiscountApplyScopeDOList.stream().collect(Collectors.groupingBy(EmsDiscountApplyScopeDO::getDiscountId));
        allDataMap.forEach((discountId, applyScopeList) -> {
            Map<String, List<EmsDiscountApplyScopeDO>> schoolMap = applyScopeList.stream()
                    .filter(o -> StringUtils.hasText(o.getSchoolId()))
                    .collect(Collectors.groupingBy(EmsDiscountApplyScopeDO::getSchoolId));
            JSONArray schoolArray = new JSONArray();
            schoolMap.forEach((school, modelList) -> {
                JSONObject schoolJson = new JSONObject();
                schoolJson.put("code", school);
                schoolJson.put("name", modelList.get(0).getSchoolName());
                JSONArray modelArray = new JSONArray();
                Map<String, List<EmsDiscountApplyScopeDO>> modelMap = modelList.stream()
                        .filter(o -> StringUtils.hasText(o.getTeachingModelId()))
                        .collect(Collectors.groupingBy(EmsDiscountApplyScopeDO::getTeachingModelId));
                modelMap.forEach((model, clazzTypeList) -> {
                    JSONObject modelJson = new JSONObject();
                    modelJson.put("code", model);
                    modelJson.put("name", clazzTypeList.get(0).getTeachingModelName());
                    modelArray.add(modelJson);
                    JSONArray clazzTypeArray = new JSONArray();
                    clazzTypeList.forEach(o -> {
                        JSONObject clazzTypeJson = new JSONObject();
                        clazzTypeJson.put("code", o.getClassTypeId());
                        clazzTypeJson.put("name", o.getClassTypeName());
                        clazzTypeArray.add(clazzTypeJson);
                    });
                    modelJson.put("children", clazzTypeArray);
                });
                schoolJson.put("children", modelArray);
                schoolArray.add(schoolJson);
            });
            discountMap.get(discountId).setApplyScopeJsonArrayStr(schoolArray.toJSONString());
        });
    }



    @Override
    public cn.itcast.ic.common.bean.Page<DiscountsDTO> findEnrolPageList(PageQueryModel<DiscountsQueryDTO> queryModel) {
        DiscountsQueryDTO queryDTO = Objects.nonNull(queryModel.getCondition()) ? queryModel.getCondition() : new DiscountsQueryDTO();
        Page<EmsDiscountsDO> page = new Page<>(queryModel.getPageNumber(), queryModel.getPageSize());
        Product product = productMapper.selectById(queryDTO.getProductId());
        Assert.notNull(product, "产品不存在");
        log.info("[报名页面查询优惠列表], 产品详情: {}", JSON.toJSON(product));
        Integer clazzId = ProductBeanUtil.getClazzId(product.getBizNo());
        Assert.notNull(clazzId, "班级ID不不存在");
        ClazzDO clazz = clazzMapper.findById(clazzId);
        EmsDiscountsDO discountsDO = new EmsDiscountsDO(clazz.getSchoolId(), clazz.getSubjectId(), clazz.getClassTypeId(), LocalDateTimeUtils.formatLocalDate(clazz.getStartDate()));
        IPage<EmsDiscountsDO> data = getBaseMapper().findEnrolPageList(page, discountsDO);
        List<DiscountsDTO> list = Objects.isNull(data) || CollectionUtil.isEmpty(data.getRecords()) ? null :
                data.getRecords().stream().map(this::transferDiscountDTO).toList();
        return cn.itcast.ic.common.bean.Page.of(queryModel.getPageNumber(), queryModel.getPageSize(), Long.valueOf(data.getTotal()).intValue(), list);
    }

    @Override
    public List<DiscountsDTO> findDiscountsByCondition(Integer clazzId) {
        EmsDiscountsDO discountsDO = this.wrapDiscountDO(clazzId) ;
        List<EmsDiscountsDO> list = getBaseMapper().getDiscountsByCondition(discountsDO) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询结果：{}" ,clazzId ,JSON.toJSONString(list));
        final BigDecimal tuitionFee = tuitionFeeService.findTuition(clazzId ,null) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询价格：{}" ,clazzId ,tuitionFee);
        return CollectionUtil.isEmpty(list) ? null :
                list.stream().map(item->this.transferDiscountDTO(item , tuitionFee)).toList();
    }
    @Override
    public DiscountTotalDTO findDiscountsByCondition(Integer clazzId, List<String> ids) {
        DiscountTotalDTO totalDTO = new DiscountTotalDTO() ;
        EmsDiscountsDO discountsDO = this.wrapDiscountDO(clazzId) ;
        List<EmsDiscountsDO> list = getBaseMapper().getDiscountsByCondition(discountsDO) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询结果：{}" ,clazzId ,JSON.toJSONString(list));
        final BigDecimal tuitionFee = tuitionFeeService.findTuition(clazzId ,null) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询价格：{}" ,clazzId ,tuitionFee);
        List<DiscountSummaryDTO> summaryList = null;
        if (CollectionUtil.isNotEmpty(ids)) {
            summaryList = new ArrayList<>();
            for (String id : ids) {
                summaryList.add(discountApi.getSummaryById(id));
            }
        }
        DiscountCalculator discountCalculator = new DiscountCalculator(summaryList) ;
        int clazzPrice = tuitionFee.multiply(BigDecimal.valueOf(100)).intValue();
        int remainMoney = CollectionUtil.isNotEmpty(summaryList) ? clazzPrice - discountCalculator.calculate(clazzPrice, clazzPrice) :clazzPrice;
        totalDTO.setSelectList( CollectionUtil.isEmpty(list) ? null :
                list.stream().map(item->this.transferDiscountDTO(item , tuitionFee,remainMoney)).toList() );
        totalDTO.setList(this.getDiscountList(discountCalculator,summaryList));
        totalDTO.setRemainMoney(BigDecimal.valueOf(remainMoney).divide(BigDecimal.valueOf(100) ,2 ,RoundingMode.HALF_UP).toString());
        return totalDTO ;
    }
     private EmsDiscountsDO wrapDiscountDO(Integer clazzId) {
         ClazzDO clazzDO  = clazzMapper.findById(clazzId) ;
         Assert.notNull(clazzDO , BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "班级ID："+ clazzId)) ;
         EmsDiscountsDO discountsDO  =new EmsDiscountsDO() ;
         discountsDO.setClasstypeId(clazzDO.getClassTypeId());
         discountsDO.setSchoolId(clazzDO.getSchoolId());
         discountsDO.setSubjectId(clazzDO.getSubjectId());
         discountsDO.setClazzDate(LocalDateTimeUtils.formatLocalDate(clazzDO.getStartDate()));
         discountsDO.setFeeDate(DateUtil.formatDate(new Date()));
         return  discountsDO ;
     }

    @Override
    public DiscountTotalDTO findV3ByParentIdByClazzId(Integer clazzId, Integer parentId ,List<String> ids) {
        DiscountTotalDTO totalDTO = new DiscountTotalDTO() ;
        EmsDiscountsDO discountsDO = this.wrapDiscountDO(clazzId) ;
        discountsDO.setRealationDiscountsId(parentId);
        List<EmsDiscountsDO> list = getBaseMapper().findByParentIdByClazzId(discountsDO) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询结果：{}" ,clazzId ,JSON.toJSONString(list));
        final BigDecimal tuitionFee = tuitionFeeService.findTuition(clazzId ,null) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询价格：{}" ,clazzId ,tuitionFee);

        List<DiscountSummaryDTO> summaryList = null;
        if (CollectionUtil.isNotEmpty(ids) ){
            summaryList = new ArrayList<>();
            for (String id : ids) {
                summaryList.add(discountApi.getSummaryById(id));
            }
        }
        DiscountCalculator  discountCalculator =  new DiscountCalculator(summaryList) ;
        int clazzPrice = tuitionFee.multiply(BigDecimal.valueOf(100)).intValue();
        int remainMoney = CollectionUtil.isNotEmpty(summaryList) ? clazzPrice - discountCalculator.calculate(clazzPrice, clazzPrice) :clazzPrice;
        totalDTO.setSelectList( CollectionUtil.isEmpty(list) ? null :
                list.stream().map(item -> this.transferDiscountDTO(item, tuitionFee,remainMoney)).toList());
        totalDTO.setList(this.getDiscountList(discountCalculator,summaryList));
        totalDTO.setRemainMoney(BigDecimal.valueOf(remainMoney).divide(BigDecimal.valueOf(100) ,2 ,RoundingMode.HALF_UP).toString());
        return totalDTO ;
    }
    @Override
    public List<DiscountsDTO> findByParentIdByClazzId(Integer clazzId, Integer parentId ,List<String> ids) {
        EmsDiscountsDO discountsDO = this.wrapDiscountDO(clazzId) ;
        discountsDO.setRealationDiscountsId(parentId);
        List<EmsDiscountsDO> list = getBaseMapper().findByParentIdByClazzId(discountsDO) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询结果：{}" ,clazzId ,JSON.toJSONString(list));
        final BigDecimal tuitionFee = tuitionFeeService.findTuition(clazzId ,null) ;
        log.info("[根据班级ID查询优惠项] 班级ID：{}, 查询价格：{}" ,clazzId ,tuitionFee);

        List<DiscountSummaryDTO> summaryList = null;
        if (CollectionUtil.isNotEmpty(ids) ){
            summaryList =ids.stream().map(discountApi::getSummaryById).toList();
        }
        int clazzPrice = tuitionFee.multiply(BigDecimal.valueOf(100)).intValue();
        int remainMoney = CollectionUtil.isNotEmpty(summaryList) ? clazzPrice - new DiscountCalculator(summaryList).calculate(clazzPrice, clazzPrice) :clazzPrice;
        return CollectionUtil.isEmpty(list) ? null :
                list.stream().map(item -> this.transferDiscountDTO(item, tuitionFee, remainMoney)).toList();
    }




    private List<DiscountsDTO> getDiscountList ( DiscountCalculator discountCalculator ,List<DiscountSummaryDTO> summaryList) {
        if(CollectionUtil.isNotEmpty(summaryList)) {
            Map<Integer, Integer>  map=  discountCalculator.getDiscountAmounts() ;
            List<DiscountsDTO> discounts =  new ArrayList<>();
            for (int i = 0; i < summaryList.size(); i++) {
                DiscountSummaryDTO summaryDTO = summaryList.get(i);
                Integer amount = map.get(summaryDTO.getId()) ;
                DiscountsDTO discountsDTO =new DiscountsDTO() ;
                discountsDTO.setDiscountsMoney(Objects.nonNull(amount) ?
                        BigDecimal.valueOf(amount).divide(BigDecimal.valueOf(100) ,2,RoundingMode.HALF_UP).toString():null);
                discountsDTO.setId(summaryDTO.getId());
                discountsDTO.setDiscountsName(summaryDTO.getName());
                discounts.add(discountsDTO) ;
            }
            return discounts ;
        }
        return  null;
    }

    @Override
    public DiscountsDTO transferDiscountDTO(EmsDiscountsDO discountsDO ) {
        DiscountsDTO discountsDTO = CustomBeanUtil.copyProperties(discountsDO, new DiscountsDTO());
        discountsDTO.setDiscountsMoney(Objects.nonNull(discountsDO.getDiscountsMoney())?
                discountsDO.getDiscountsMoney().setScale(2, RoundingMode.HALF_UP).toString(): null);
        discountsDTO.setDiscountsRate(Objects.nonNull(discountsDO.getDiscountsRate())?
                BigDecimal.valueOf(discountsDO.getDiscountsRate()).setScale(2, RoundingMode.HALF_UP).toString(): null);
        discountsDTO.setClazzBeginDate(discountsDO.getClazzBeginDate());
        return discountsDTO ;
    }

    /**
     * 计算和格式化 优惠项
     * 需要判断当前优惠项金额是否大于剩余金额 ，  如果大于 优惠项金额 = 剩余金额  ， 否则显示原优惠金额
     * @param discountsDO 数据库优惠信息
     * @param tuitionFee 班级执行价格
     * @param remainMoney 应缴金额
     * @return 优惠DTO ，用于页面回显或者逻辑处理
     */
    @Override
    public DiscountsDTO transferDiscountDTO(EmsDiscountsDO discountsDO, BigDecimal tuitionFee, Integer remainMoney) {
        DiscountsDTO discountsDTO = CustomBeanUtil.copyProperties(discountsDO, new DiscountsDTO());
        discountsDTO.setDiscountsMoney(Objects.nonNull(discountsDO.getDiscountsMoney()) ?
                discountsDO.getDiscountsMoney().setScale(2, RoundingMode.HALF_UP).toString() : null);
        discountsDTO.setDiscountsRate(Objects.nonNull(discountsDO.getDiscountsRate()) ?
                BigDecimal.valueOf(discountsDO.getDiscountsRate()).setScale(2, RoundingMode.HALF_UP).toString() : null);
        discountsDTO.setClazzBeginDate(discountsDO.getClazzBeginDate());
        discountsDTO.setShowDiscountMoney(discountsDTO.getDiscountsMoney());
        if (discountsDO.getDiscountsRate() > 0) {
            discountsDTO.setShowDiscountMoney(tuitionFee.multiply(BigDecimal.valueOf(discountsDO.getDiscountsRate())).setScale(2, RoundingMode.HALF_UP).toString());
        }
        if (StringUtils.hasText(discountsDTO.getShowDiscountMoney()) ) {
            BigDecimal remainBigdecimal  = Objects.nonNull(remainMoney) ? BigDecimal.valueOf(remainMoney).divide(BigDecimal.valueOf(100),4,RoundingMode.HALF_UP) : tuitionFee ;
            if (BigDecimal.valueOf(Double.parseDouble(discountsDTO.getShowDiscountMoney())).compareTo(remainBigdecimal) > 0) {
                discountsDTO.setShowDiscountMoney(remainBigdecimal.setScale(2, RoundingMode.HALF_UP).toString());
            }
        }
        return discountsDTO;
    }

    @Override
    public DiscountsDTO transferDiscountDTO(EmsDiscountsDO discountsDO , BigDecimal tuitionFee) {
        DiscountsDTO discountsDTO = CustomBeanUtil.copyProperties(discountsDO, new DiscountsDTO());
        discountsDTO.setDiscountsMoney(Objects.nonNull(discountsDO.getDiscountsMoney())?
                discountsDO.getDiscountsMoney().setScale(2, RoundingMode.HALF_UP).toString(): null);
        discountsDTO.setDiscountsRate(Objects.nonNull(discountsDO.getDiscountsRate())?
                BigDecimal.valueOf(discountsDO.getDiscountsRate()).toString(): null);
        discountsDTO.setClazzBeginDate(discountsDO.getClazzBeginDate());
        discountsDTO.setShowDiscountMoney(discountsDTO.getDiscountsMoney());
        if(Objects.nonNull(discountsDO.getDiscountsRate()) && discountsDO.getDiscountsRate() >0) {
            discountsDTO.setShowDiscountMoney(tuitionFee.multiply(BigDecimal.valueOf(discountsDO.getDiscountsRate())).setScale(2, RoundingMode.HALF_UP).toString());
        }
        log.info("【计算页面回显优惠金额】 结果：{}" ,JSON.toJSONString(discountsDTO));
        return discountsDTO ;
    }
}
